import GlobalConfig from '../../conf'
import Table from '../../table'
import Grid from '../../grid'
import XEUtils from 'xe-utils'
import platformMixins from "./platformMixins"

function getTableOns(_vm) {
  const { $listeners } = _vm
  const ons = {}
  XEUtils.each($listeners, (cb, type) => {
    ons[type] = (...args) => {
      _vm.$emit(type, ...args)
    }
  })
  ons['checkbox-all'] = _vm.checkboxAllEvent
  ons['checkbox-change'] = _vm.checkboxChangeEvent
  return ons
}


function hasChilds(_vm, row) {
  const childList = row[_vm.treeOpts.children]
  return childList && childList.length
}

function registerComponent(vcutable) {
  const propKeys = Object.keys(Table.props).filter(name => ['data', 'treeConfig'].indexOf(name) === -1)
  const options = {
    name: 'VcuTableVirtualTree',
    extends: Grid,
    mixins: [platformMixins],
    data() {
      return {
        removeList: [],
        treeLazyLoadeds: []
      }
    },
    computed: {
      treeOpts() {
        return Object.assign({}, GlobalConfig.table.treeConfig, this.treeConfig)
      },
      checkboxOpts() {
        return Object.assign({}, GlobalConfig.table.checkboxConfig, this.checkboxConfig)
      },
      tableExtendProps() {
        let rest = {}
        propKeys.forEach(key => {
          rest[key] = this[key]
        })
        if (rest.checkboxConfig) {
          rest.checkboxConfig = this.checkboxOpts
        }
        return rest
      }
    },
    watch: {
      columns(value) {
        this.handleColumns(value)
      },
      data(value) {
        this.loadData(value)
      }
    },
    created() {
      const { treeOpts, data, columns } = this
      Object.assign(this, {
        fullTreeData: [],
        treeTableData: [],
        fullTreeRowMap: new Map()
      })
      if (columns) {
        this.handleColumns(columns)
      }
      if (data) {
        this.reloadData(data)
      }
    },
    mounted() {

    },
    destroyed() {

    },
    render(h) {
      const { $scopedSlots } = this
      return h('div', {
        class: ['vcu-table-virtual-tree', {
          'is--animat': !!this.animat,
          'is--round': this.round,
        }],
      }, [
        /**
         * 渲染表格
         */
        h('vcu-table', {
          props: this.tableProps,
          on: getTableOns(this),
          scopedSlots: $scopedSlots,
          ref: 'virtualTreeTable'
        })

      ])
    },
    methods: {
      loadColumn(columns) {
        return this.$nextTick().then(() => {
          const { $vcu, $scopedSlots, renderTreeIcon, treeOpts } = this
          XEUtils.eachTree(columns, column => {
            if (column.treeNode) {
              if (!column.slots) {
                column.slots = {}
              }
              column.slots.icon = renderTreeIcon
            }
          }, treeOpts)
          this.$refs.virtualTreeTable.loadColumn(columns)
        })
      },
      renderTreeIcon(params, h, cellVNodes) {
        const { treeLazyLoadeds, treeOpts } = this
        let { isHidden, row } = params
        const { children, hasChild, indent, lazy, trigger, iconLoaded, showIcon, iconOpen, iconClose } = treeOpts
        let rowChilds = row[children]
        let hasLazyChilds = false
        let isAceived = false
        let isLazyLoaded = false
        let on = {}
        if (!isHidden) {
          isAceived = row._X_EXPAND
          if (lazy) {
            isLazyLoaded = treeLazyLoadeds.indexOf(row) > -1
            hasLazyChilds = row[hasChild]
          }
        }
        if (!trigger || trigger === 'default') {
          on.click = (evnt) => this.triggerTreeExpandEvent(evnt, params)
        }
        return [
          h('div', {
            class: ['vcu-cell--tree-node', {
              'is--active': isAceived
            }],
            style: {
              paddingLeft: `${row._X_LEVEL * indent}px`
            }
          }, [
            showIcon && ((rowChilds && rowChilds.length) || hasLazyChilds) ? [
              h('div', {
                class: 'vcu-tree--btn-wrapper',
                on
              }, [
                h('i', {
                  class: ['vcu-tree--node-btn', isLazyLoaded ? (iconLoaded || GlobalConfig.icon.TABLE_TREE_LOADED) : (isAceived ? (iconOpen || GlobalConfig.icon.TABLE_TREE_OPEN) : (iconClose || GlobalConfig.icon.TABLE_TREE_CLOSE))]
                })
              ])
            ] : null,
            h('div', {
              class: 'vcu-tree-cell'
            }, cellVNodes)
          ])
        ]
      },
      handleColumns(columns) {
        const { $vcu, renderTreeIcon, checkboxOpts } = this
        if (columns) {
          if ((!checkboxOpts.checkField || !checkboxOpts.halfField) && columns.some(conf => conf.type === 'checkbox')) {
            console.error($vcu.t('vcu.error.reqProp', ['table.checkbox-config.checkField | table.checkbox-config.halfField']))
            return []
          }
          const treeNodeColumn = columns.find(conf => conf.treeNode)
          if (treeNodeColumn) {
            let slots = treeNodeColumn.slots || {}
            slots.icon = renderTreeIcon
            treeNodeColumn.slots = slots
            this.treeNodeColumn = treeNodeColumn
          }
          return columns
        }
        return []
      },


      _loadTreeData(data) {
        const { highlightCurrentRow } = this
        const selectRow = this.getRadioRecord()
        let currentRow
        if (highlightCurrentRow) {
          currentRow = this.getCurrentRecord()
        }
        return this.$nextTick()
          .then(() => this.$refs.virtualTreeTable.loadData(data))
          .then(() => {
            if (selectRow) {
              this.setRadioRow(selectRow)
            }
            if (highlightCurrentRow && currentRow) {
              this.setCurrentRow(currentRow)
            }
          })
      },
      getData(rowIndex) {
        const { fullTreeData } = this
        return XEUtils.isUndefined(rowIndex) ? fullTreeData.slice(0) : fullTreeData[rowIndex]
      },
      loadData(data) {
        return this._loadTreeData(this.toVirtualTree(data)).then(() => {
          this.handleDefaultTreeExpand()
        })
      },
      reloadData(data) {
        return this.$nextTick()
          .then(() => this.$refs.virtualTreeTable.reloadData(this.toVirtualTree(data)))
          .then(() => this.handleDefaultTreeExpand())
      },
      /**
       * 定义树属性
       */
      toVirtualTree(treeData) {
        const { treeOpts } = this
        let fullTreeRowMap = this.fullTreeRowMap
        fullTreeRowMap.clear()
        XEUtils.eachTree(treeData, (item, index, items, paths, parent, nodes) => {
          item._X_LOADED = false
          item._X_EXPAND = false
          item._X_INSERT = false
          item._X_LEVEL = nodes.length - 1
          fullTreeRowMap.set(item, { item, index, items, paths, parent, nodes })
        }, treeOpts)
        this.fullTreeData = treeData.slice(0)
        this.treeTableData = treeData.slice(0)
        return treeData
      },
      isTreeExpandByRow(row) {
        return !!row._X_EXPAND
      },
      setTreeExpansion(rows, expanded) {
        return this.setTreeExpand(rows, expanded)
      },
      handleAsyncTreeExpandChilds(row) {
        const { treeLazyLoadeds, treeOpts, checkboxOpts } = this
        const { loadMethod, children } = treeOpts
        const { checkStrictly } = checkboxOpts
        const recursionChildren = (val, LEVEL) =>{
          return val.map(item => {
            item._X_LOADED = false
            item._X_EXPAND = false
            item._X_INSERT = false
            item._X_LEVEL = LEVEL + 1
            if(item.children && item.children.length) {
              item.children = recursionChildren(item.children, LEVEL + 1)
            }
            return item
          })
        }
        return new Promise(resolve => {
          if (loadMethod) {
            treeLazyLoadeds.push(row)
            loadMethod({ row }).catch(() => []).then((childs) => {
              row._X_LOADED = true
              XEUtils.remove(treeLazyLoadeds, item => item === row)
              if (!XEUtils.isArray(childs)) {
                childs = []
              }
              if (childs) {
                row[children] = recursionChildren(childs,row._X_LEVEL)
                if (childs.length && !row._X_EXPAND) {
                  this.virtualExpand(row, true)
                }
                // 如果当前节点已选中，则展开后子节点也被选中
                if (!checkStrictly && this.isCheckedByCheckboxRow(row)) {
                  this.setCheckboxRow(childs, true)
                }
              }
              resolve(this.$nextTick().then(() => this.recalculate()))
            })
          } else {
            resolve(null)
          }
        })
      },
      setTreeExpand(rows, expanded) {
        const { treeLazyLoadeds, treeOpts, tableFullData, treeNodeColumn } = this
        const { lazy, hasChild, accordion, toggleMethod } = treeOpts
        const result = []
        if (rows) {
          if (!XEUtils.isArray(rows)) {
            rows = [rows]
          }
          const columnIndex = this.getColumnIndex(treeNodeColumn)
          const $columnIndex = this.$getColumnIndex(treeNodeColumn)
          let validRows = toggleMethod ? rows.filter((row) => toggleMethod({ expanded, column: treeNodeColumn, row, columnIndex, $columnIndex })) : rows
          if (accordion) {
            validRows = validRows.length ? [validRows[validRows.length - 1]] : []
            // 同一级只能展开一个
            const matchObj = XEUtils.findTree(tableFullData, item => item === rows[0], treeOpts)
            if (matchObj) {
              matchObj.items.forEach((row) => {
                row._X_EXPAND = false
              })
            }
          }
          validRows.forEach((row) => {
            const isLoad = lazy && row[hasChild] && !row._X_LOADED && treeLazyLoadeds.indexOf(row) === -1
            // 是否使用懒加载
            if (expanded && isLoad) {
              result.push(this.handleAsyncTreeExpandChilds(row))
            } else {
              if (hasChilds(this, row)) {
                this.virtualExpand(row, !!expanded)
              }
            }
          })
          return Promise.all(result).then(() => {
            this._loadTreeData(this.treeTableData)
            return this.recalculate()
          })
        }
        return this.$nextTick()
      },
      setAllTreeExpansion(expanded) {
        return this.setAllTreeExpand(expanded)
      },
      setAllTreeExpand(expanded) {
        return this._loadTreeData(this.virtualAllExpand(expanded))
      },
      toggleTreeExpansion(row) {
        return this.toggleTreeExpand(row)
      },
      triggerTreeExpandEvent(evnt, params) {
        const { treeOpts, treeLazyLoadeds } = this
        const { row, column } = params
        const { lazy } = treeOpts
        if (!lazy || treeLazyLoadeds.indexOf(row) === -1) {
          const expanded = !this.isTreeExpandByRow(row)
          this.setTreeExpand(row, expanded)
          this.$emit('toggle-tree-expand', { expanded, column, row, $event: evnt })
        }
      },
      toggleTreeExpand(row) {
        return this._loadTreeData(this.virtualExpand(row, !row._X_EXPAND))
      },
      getTreeExpandRecords() {
        const { fullTreeData, treeOpts } = this
        const treeExpandRecords = []
        XEUtils.eachTree(fullTreeData, row => {
          if (row._X_EXPAND && hasChilds(this, row)) {
            treeExpandRecords.push(row)
          }
        }, treeOpts)
        return treeExpandRecords
      },
      clearTreeExpand() {
        return this.setAllTreeExpand(false)
      },
      scrollTo(scrollLeft, scrollTop){
        return this.$refs.virtualTreeTable.scrollTo(scrollLeft, scrollTop)
      },
      scrollToRow(row){
        this.$refs.virtualTreeTable.scrollToRow(row)
        return this.$nextTick()
      },
      getScroll(){
        return this.$refs.virtualTreeTable.getScroll()
      },
      scrollToCondition(condition) {
        this.$refs.virtualTreeTable.scrollToCondition(condition)
        return this.$nextTick()
      },
      /**
       * 获取表格数据集，包含新增、删除
       * 不支持修改
       */
      getRecordset() {
        return {
          insertRecords: this.getInsertRecords(),
          removeRecords: this.getRemoveRecords(),
          updateRecords: []
        }
      },
      isInsertByRow(row) {
        return !!row._X_INSERT
      },
      getInsertRecords() {
        const { treeOpts } = this
        const insertRecords = []
        XEUtils.eachTree(this.fullTreeData, row => {
          if (row._X_INSERT) {
            insertRecords.push(row)
          }
        }, treeOpts)
        return insertRecords
      },
      insert(records) {
        return this.insertAt(records, null)
      },
      /**
       * 插入子级数据
       */
      insertAtChild(records, row, expand=true) {
        const { $vcu, fullTreeData, treeOpts } = this
        const _expandRecords = this.getTreeExpandRecords()
        let matchObj = XEUtils.findTree(fullTreeData, item => item === row, treeOpts)

        if (!matchObj || matchObj.index === -1) {
          throw new Error($vcu.t('vcu.error.unableInsert'))
        }

        let newRecords = records.map((record) => Object.assign({
          _X_LOADED: false,
          _X_EXPAND: false,
          _X_INSERT: true,
          _X_LEVEL: 0,
          _VCUID: XEUtils.uniqueId('row_')
        }, record))
        if(row.hasChild) row.hasChild = undefined;
        if (row.children) {
          row.children.push(...newRecords)
        } else {
          row.children = newRecords
        }
        _expandRecords.push(row)
        return this.loadData(fullTreeData).then(() => {
          expand && this.setTreeExpand(_expandRecords, true)
          return {
            rows: _expandRecords,
            row: newRecords[0]
          }
        })
      },
      /**
       * 支持任意层级插入与删除
       */
      insertAt(records, row) {
        const { $vcu, fullTreeData, treeTableData, treeOpts } = this
        if (!XEUtils.isArray(records)) {
          records = [records]
        }
        /* let newRecords = records.map((record) => this.defineField(Object.assign({
          _X_LOADED: false,
          _X_EXPAND: false,
          _X_INSERT: true,
          _X_LEVEL: 0
        }, record))) */
        let newRecords = records.map((record) => Object.assign({
          _X_LOADED: false,
          _X_EXPAND: false,
          _X_INSERT: true,
          _X_LEVEL: 0,
          _VCUID: XEUtils.uniqueId('row_')
        }, record))

        if (!row) {
          fullTreeData.unshift(...newRecords)
          treeTableData.unshift(...newRecords)
        } else {
          if (row === -1) {
            fullTreeData.push(...newRecords)
            treeTableData.push(...newRecords)
          } else {
            let matchObj = XEUtils.findTree(fullTreeData, item => item === row, treeOpts)
            if (!matchObj || matchObj.index === -1) {
              throw new Error($vcu.t('vcu.error.unableInsert'))
            }
            let { items, index, nodes } = matchObj
            let rowIndex = treeTableData.indexOf(row)
            if (rowIndex > -1) {
              treeTableData.splice(rowIndex, 0, ...newRecords)
            }
            items.splice(index, 0, ...newRecords)
            newRecords.forEach((item) => {
              item._X_LEVEL = nodes.length - 1
            })
          }
        }
        return this._loadTreeData(treeTableData).then(() => {
          return {
            row: newRecords.length ? newRecords[newRecords.length - 1] : null,
            rows: newRecords
          }
        })
      },
      /**
       * 获取已删除的数据
       */
      getRemoveRecords() {
        return this.removeList
      },
      removeSelecteds() {
        return this.removeCheckboxRow()
      },
      /**
       * 删除选中数据
       */
      removeCheckboxRow() {
        return this.remove(this.getCheckboxRecords()).then((params) => {
          return params
        })
      },
      remove(rows) {
        const { removeList, fullTreeData, treeOpts } = this
        let rest = []
        if (!rows) {
          rows = fullTreeData
        } else if (!XEUtils.isArray(rows)) {
          rows = [rows]
        }
        rows.forEach((row) => {
          let matchObj = XEUtils.findTree(fullTreeData, item => item === row, treeOpts)
          if (matchObj) {
            const { item, items, index, parent } = matchObj
            if (!this.isInsertByRow(row)) {
              removeList.push(row)
            }
            if (parent) {
              let isExpand = this.isTreeExpandByRow(parent)
              if (isExpand) {
                this.handleCollapsing(parent)
              }
              items.splice(index, 1)
              if (isExpand) {
                this.handleExpanding(parent)
              }
            } else {
              this.handleCollapsing(item)
              items.splice(index, 1)
              this.treeTableData.splice(this.treeTableData.indexOf(item), 1)
            }
            rest.push(item)
          }
        })
        return this._loadTreeData(this.treeTableData).then(() => {
          return { row: rest.length ? rest[rest.length - 1] : null, rows: rest }
        })
      },
      /**
       * 处理默认展开树节点
       */
      handleDefaultTreeExpand() {
        let { treeConfig, treeOpts, tableFullData } = this
        if (treeConfig) {
          let { children, expandAll, expandRowKeys } = treeOpts
          if (expandAll) {
            this.setAllTreeExpand(true)
          } else if (expandRowKeys && this.rowId) {
            let rowkey = this.rowId
            expandRowKeys.forEach((rowid) => {
              let matchObj = XEUtils.findTree(tableFullData, item => rowid === XEUtils.get(item, rowkey), treeOpts)
              let rowChildren = matchObj ? matchObj.item[children] : 0
              if (rowChildren && rowChildren.length) {
                this.setTreeExpand(matchObj.item, true)
              }
            })
          }
        }
      },
      /**
       * 展开/收起树节点
       */
      virtualExpand(row, expanded) {
        const { treeOpts, treeNodeColumn } = this
        const { toggleMethod } = treeOpts
        const columnIndex = this.getColumnIndex(treeNodeColumn)
        const $columnIndex = this.$getColumnIndex(treeNodeColumn)
        if (!toggleMethod || toggleMethod({ expanded, row, column: treeNodeColumn, columnIndex, $columnIndex })) {
          if (row._X_EXPAND !== expanded) {
            if (row._X_EXPAND) {
              this.handleCollapsing(row)
            } else {
              this.handleExpanding(row)
            }
          }
        }
        return this.treeTableData
      },
      // 展开节点
      handleExpanding(row) {
        if (hasChilds(this, row)) {
          const { treeTableData, treeOpts } = this
          let childRows = row[treeOpts.children]
          let expandList = []
          let rowIndex = treeTableData.indexOf(row)
          if (rowIndex === -1) {
            throw new Error('Expanding error')
          }
          const expandMaps = new Map()
          XEUtils.eachTree(childRows, (item, index, obj, paths, parent, nodes) => {
            if (!parent || (parent._X_EXPAND && expandMaps.has(parent))) {
              expandMaps.set(item, 1)
              expandList.push(item)
            }
          }, treeOpts)
          row._X_EXPAND = true
          treeTableData.splice(rowIndex + 1, 0, ...expandList)
        }
        return this.treeTableData
      },
      // 收起节点
      handleCollapsing(row) {
        if (hasChilds(this, row)) {
          const { treeTableData, treeOpts } = this
          let childRows = row[treeOpts.children]
          let nodeChildList = []
          XEUtils.eachTree(childRows, item => {
            nodeChildList.push(item)
          }, treeOpts)
          row._X_EXPAND = false
          this.treeTableData = treeTableData.filter((item) => nodeChildList.indexOf(item) === -1)
        }
        return this.treeTableData
      },
      /**
       * 展开/收起所有树节点
       */
      virtualAllExpand(expanded) {
        const { treeOpts } = this
        if (expanded) {
          const tableList = []
          XEUtils.eachTree(this.fullTreeData, row => {
            row._X_EXPAND = expanded
            tableList.push(row)
          }, treeOpts)
          this.treeTableData = tableList
        } else {
          XEUtils.eachTree(this.fullTreeData, row => {
            row._X_EXPAND = expanded
          }, treeOpts)
          this.treeTableData = this.fullTreeData.slice(0)
        }
        return this.treeTableData
      },
      clearCheckboxRow() {
        return this.setAllCheckboxRow(false)
      },
      toggleAllCheckboxRow() {
        const { checkboxOpts } = this
        const { checkField, checkStrictly } = checkboxOpts
        if (checkField && !checkStrictly) {
          return this.setAllCheckboxRow(!this.fullTreeData.every((row) => row[checkField]))
        }
        return this.$nextTick()
      },
      setAllCheckboxRow(checked) {
        const { checkboxOpts, treeOpts } = this
        const { checkField, halfField, checkStrictly } = checkboxOpts
        if (checkField && !checkStrictly) {
          XEUtils.eachTree(this.fullTreeData, row => {
            row[checkField] = checked
            if (halfField) {
              row[halfField] = false
            }
          }, treeOpts)
          this.$refs.virtualTreeTable.checkSelectionStatus()
        }
        this.$forceUpdate()
        return this.$nextTick()
      },
      checkboxAllEvent(params) {
        const { checked } = params
        this.setAllCheckboxRow(checked)
        this.$emit('checkbox-all', params)
      },
      checkboxChangeEvent(params) {
        const { row, checked } = params
        this.setCheckboxRow(row, checked)
        this.$emit('checkbox-change', params)
      },
      toggleCheckboxRow(rows) {
        const { checkboxOpts } = this
        const { checkField } = checkboxOpts
        if (checkField) {
          rows.forEach((row) => {
            this.setCheckboxRow(row, !row[checkField])
          })
        }
      },
      setCheckboxRow(rows, checked) {
        const { checkboxOpts, treeOpts } = this
        const { checkField, halfField, checkStrictly } = checkboxOpts
        if (!XEUtils.isArray(rows)) {
          rows = [rows]
        }
        if (checkField) {
          if (checkStrictly) {
            rows.forEach((row) => {
              row[checkField] = checked
              if (halfField) {
                row[halfField] = false
              }
            })
          } else {
            XEUtils.eachTree(rows, row => {
              row[checkField] = checked
              if (halfField) {
                row[halfField] = false
              }
            }, treeOpts)
            rows.forEach((row) => {
              this.checkParentNodeSelection(row)
            })
          }
        }
        this.$forceUpdate()
        return this.$nextTick()
      },
      checkParentNodeSelection(row) {
        const { checkboxOpts, treeOpts } = this
        const { children } = treeOpts
        const { checkField, halfField, checkStrictly } = checkboxOpts
        const matchObj = XEUtils.findTree(this.fullTreeData, item => item === row, treeOpts)
        if (matchObj && checkField && !checkStrictly) {
          const parentRow = matchObj.parent
          if (parentRow) {
            const isAll = parentRow[children].every((item) => item[checkField])
            if (halfField && !isAll) {
              parentRow[halfField] = parentRow[children].some((item) => item[checkField] || item[halfField])
            }
            parentRow[checkField] = isAll
            this.checkParentNodeSelection(parentRow)
          } else {
            this.$refs.virtualTreeTable.checkSelectionStatus()
          }
        }
      },
      getCheckboxRecords() {
        const { checkboxOpts, treeOpts } = this
        const { checkField } = checkboxOpts
        if (checkField) {
          const records = []
          XEUtils.eachTree(this.fullTreeData, row => {
            if (row[checkField]) {
              records.push(row)
            }
          }, treeOpts)
          return records
        }
        return this.$refs.virtualTreeTable.getCheckboxRecords()
      },
      getCheckboxIndeterminateRecords() {
        const { checkboxOpts, treeOpts } = this
        const { halfField } = checkboxOpts
        if (halfField) {
          const records = []
          XEUtils.eachTree(this.fullTreeData, row => {
            if (row[halfField]) {
              records.push(row)
            }
          }, treeOpts)
          return records
        }
        return this.$refs.virtualTreeTable.getCheckboxIndeterminateRecords()
      },
      setCurrentRow(row, change) {
        return this.$refs.virtualTreeTable.setCurrentRow(row, change)
      },
      setActiveRow(row, opts) {
        return this.$refs.virtualTreeTable.setActiveRow(row, opts)
      },
      validate(type) {
        return this.$refs.virtualTreeTable.validate(type)
      },
      getCurrentRecord() {
        return this.$refs.virtualTreeTable.getCurrentRecord()
      },
      getActiveRecord() {
        return this.$refs.virtualTreeTable.getActiveRecord()
      }
    }
  }
  vcutable.Vue.component(options.name, options);
}

var VCUTablePluginVirtualTree = {
  install: function install(vcutable) {
    registerComponent(vcutable);
  }
};
export { VCUTablePluginVirtualTree };

if (typeof window !== 'undefined' && window.VCUTable && window.VCUTable.use) {
  window.VCUTable.use(VCUTablePluginVirtualTree);
}

export default VCUTablePluginVirtualTree;
